#pragma once 
#include <bits/stdc++.h>

class ThreadPool{
public:
    /**
     * @brief 创建指定线程数量的线程池对象
     * 
     * @param thread_num   创建的线程池包含的线程数量
     */
    ThreadPool(size_t thread_num);


    /**
     * @brief  向线程添加任务
     * 
     * @tparam Func 要执行的任务函数
     * @tparam Args 任务函数的参数
     * @return std::future<typename std::result_of<Func(Args...)>::type>  返回任务的future对象
     */ 
    template<typename Func, typename... Args>
    auto enqueue(Func&& func, Args&&... args) -> std::future<typename std::result_of<Func(Args...)>::type>;


    /**
     * @brief 销毁线程池对象
     * 
     */
    ~ThreadPool();
private:
    // 线程池中的线程
    std::vector<std::thread>          _workers;

    // 任务队列, 即需要使用线程池完成的任务
    std::queue<std::function<void()>> _tasks;

    // 不同线程访问同一个任务队列, 因此需要互斥锁
    std::mutex                        _mutex;
    
    // 需要等待有活儿干或者别的信号
    std::condition_variable           _cond;

    // flag  是否要退出了
    bool _stop;
};


inline ThreadPool::ThreadPool(size_t thread_num) : _stop(false) {
    // 遍历每个线程, 并定义线程的工作模式
    // 1. 干完活了就从任务队列头部再取一个继续干(一次for循环结束了, 继续进入下一次循环)   //! 死循环 
    // 2. 干完活了，任务队列也为空就睡觉休息
    for (size_t i = 0; i < thread_num; i++) {
        _workers.emplace_back([this](){           // 使用lambda表达式创建线程对象
            for(;;) {                             // 每个线程无限循环
                std::function<void()> task;
                {
                    std::unique_lock<std::mutex> locker(this->_mutex);
                    this->_cond.wait(locker, [this](){     //! wait操作会休眠, 因此死循环不会造成过度的硬件资源消耗
                        //! 开始睡觉, 除非
                        // 1. 有人修改了_stop, 表示线程池要退出了
                        // 2. 任务队列不为空， 就是有活儿干了
                        return this->_stop || !this->_tasks.empty();
                    });
                    // 1. 如果是要退出而不是有活儿干, 那就退出
                    if (this->_stop && this->_tasks.empty())
                        return;
                    
                    // 2. 如果是有活儿干, 那就取一个任务, 并将其从队列中移除
                    task = std::move(this->_tasks.front());
                    this->_tasks.pop();
                }
                task();  //!  开始干活
            }
        });
    } // for
}


template<class Func, class... Args>
auto ThreadPool::enqueue(Func&& func, Args&&... args) 
    -> std::future<typename std::result_of<Func(Args...)>::type> {
    using return_type = typename std::result_of<Func(Args...)>::type;

    // 创建一个任务对象
    auto task = std::make_shared<std::packaged_task<return_type()>>(
        std::bind(std::forward<Func>(func), std::forward<Args>(args)...)
    );

    std::future<return_type> res = task->get_future();
    {
        std::unique_lock<std::mutex> locker(this->_mutex);
        if (this->_stop)
            throw std::runtime_error("enqueue on a stopped thread pool");
        // 从智能指针获取原始对象并调用
        this->_tasks.emplace([task](){ (*task)(); });
    }

    _cond.notify_one();   // 随机唤醒一个没事做在睡觉的线程
    return res;           // 返回一个future对象
}


inline ThreadPool::~ThreadPool() {
    {
        std::unique_lock<std::mutex> locker(_mutex);
        _stop = true;
    }
    _cond.notify_all();          // 唤醒所在休眠的线程
    for(auto& worker : _workers) {
        worker.join();
    }
}